﻿using GrayParrot.Core.Data;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace GrayParrot.Data.ADONET
{
    public abstract class Repository<TEntity, TKey> : IRepository<TEntity, TKey> where TEntity : class, new() where TKey : class
    {
        DbContext _context;
        public bool AutoMap { get; set; }

        public Repository(DbContext context)
        {
            _context = context;
        }

        //public Repository(DbContext context, bool autoMap):this(context)
        //{
        //    autoMapEntity = autoMap;
        //}


        //protected void Inizialize(bool autoMap)
        //{
        //    autoMapEntity = autoMap;
        //}

        protected DbContext Context { get { return _context; } }

        protected IEnumerable<TEntity> ToList(IDbCommand command)
        {
            using (var reader = command.ExecuteReader())
            {
                List<TEntity> items = new List<TEntity>();
                while (reader.Read())
                {
                    var item = new TEntity();
                    if(AutoMap)
                        AutoMapEntity<TEntity>(item, reader);
                    else
                        Map(reader, item);
                    
                    items.Add(item);
                }
                return items;
            }
        }

        protected TEntity ToEntity(IDbCommand command)
        {
            using (var reader = command.ExecuteReader())
            {
                var item = new TEntity();
                while (reader.Read())
                {
                    if (AutoMap)
                        AutoMapEntity<TEntity>(item, reader);
                    else
                        Map(reader, item);
                }
                return item;
            }
        }

        protected string GetExecutingMethodName(Exception exception)
        {
            var trace = new System.Diagnostics.StackTrace(exception);
            var frame = trace.GetFrame(0);
            var method = frame.GetMethod();

            return string.Concat(method.DeclaringType.FullName, ".", method.Name);
        }

        protected abstract void Map(IDataRecord record, TEntity entity);

        private void AutoMapEntity<TEntity>(TEntity entityToMap, IDataRecord currentRecord) 
        {
            foreach (PropertyInfo prop in entityToMap.GetType().GetProperties())
            {
                if (prop.PropertyType.Name.ToString() != @"ICollection`1")
                {
                    if (!object.Equals(currentRecord[prop.Name], DBNull.Value))
                    {
                        prop.SetValue(entityToMap, currentRecord[prop.Name], null);
                    }
                }
            }
        }

        private void AutoMapEntity<TEntity>(TEntity entityToMap, IDataReader currentRecord)
        {
            foreach (PropertyInfo prop in entityToMap.GetType().GetProperties())
            {
                if (prop.PropertyType.Name.ToString() != @"ICollection`1")
                {
                    if (!object.Equals(currentRecord[prop.Name], DBNull.Value))
                    {
                        prop.SetValue(entityToMap, currentRecord[prop.Name], null);
                    }
                }
            }
        }

        public abstract void Add(TEntity item);

        public abstract void Remove(TKey key);

        public abstract void Remove(TEntity item);

        public abstract void Update(TEntity item);

        public abstract TEntity Find(TKey key);

        public abstract IEnumerable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate);

        public abstract IEnumerable<TEntity> FindAll();
    }
}
